package thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ReentrantLock代码。
 *  1：获取锁【lock.lock()】方法，释放锁【lock.unlock()】，而且需要在【finally代码块中执行，保证可以被解锁】。
 *  2：ReentrantLock可以重入，也就是自己不会把自己锁住，【synchronized】也是可重入的。
 *  3：可打断：使用【lock.lockInterruptibly();】获取【一个可以中断的锁】。
 *          这个时候【别的线程，调用该线程的interrupt方法】，就可以中断，获取锁的过程了。【源码解析在juc包中介绍】。
 *  4：可以设置锁的超时：使用【lock.tryLock()方法】，设置锁的最大获取时间。
 *  5：设置是否是公平锁：【默认为非公平锁（性能好）】、【公平锁：是先来的线程先获取锁】、【非公平锁：不保证先到先得】。
 *  6：多个条件变量：【synchronized】中也有条件变量，就是我们讲原理时，那个【WaitSet】，当条件不满足时进入WaitSet等待。
 *      ReentrantLock的条件变量比synchronized的强大之处在于，【它支持多个条件变量的】。
 *  后续的代码见【LockTest2.java】
 */
public class LockTest {

    //一个非公平锁
    ReentrantLock reentrantLock = new ReentrantLock(false);

    /**
     * 获取锁和释放锁的基本语法。
     */
    private void lock(){
        try {
            // 获取锁
            reentrantLock.lock();
            // 临界区
            // do something
        } finally {
            // 释放锁
            reentrantLock.unlock();
        }
    }

    /**
     * 可重入：可以重入，不会自己把自己锁住。
     */
    private void lock1(){
        try {
            reentrantLock.lock();
            //do something
            lock2();
        }finally {
            reentrantLock.unlock();
        }
    }

    private void lock2() {
        try {
            reentrantLock.lock();
            //do something
        }finally {
            reentrantLock.unlock();
        }
    }

    /**
     * 可中断：获取锁的方式可以是【lock.lockInterruptibly();】可中断。
     * 这个方法表示，如果别的线程调用了【t1.interrupt】中断方法。那么这个线程就不会继续获取这个锁了。
     * 就会把自己打断。【lock.lock();】方法，则不会打断获取锁。
     */
    private void lock3(){
        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            System.out.println("启动...");
            try {
                //没有竞争就会获取锁
                //有竞争就进入阻塞队列等待,但可以被打断
                lock.lockInterruptibly();
                //lock.lock(); //不可打断
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("等锁的过程中被打断");
                return;
            }

            try {
                System.out.println("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        System.out.println("获得了锁");
        t1.start();

        try {
            Thread.sleep(1000);
            System.out.println("执行打断");
            //打断t1线程。
            t1.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
            t1.interrupt();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 可超时：使用【lock.tryLock()】方法，可以设置一个最大的获取锁的时间，
     *      如果不设置，默认立即返回。（即没有获取锁就直接返回失败）。
     */
    private void lock4(){
        ReentrantLock lock = new ReentrantLock();

        Thread t1 = new Thread(() -> {
            System.out.println("启动...");
            try {
                if (!lock.tryLock(1, TimeUnit.SECONDS)) {
                    System.out.println("获取立刻失败，返回");
                    return;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                //获取锁的时候，出现了中断，直接返回。
                return;
            }
            try {
                System.out.println("获得了锁");
            } finally {
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        System.out.println("获得了锁");
        t1.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}
